package org.dromara.llm.service.impl;

import cn.hutool.core.lang.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.llm.domain.LlmSender;
import org.dromara.llm.domain.TtsFlagCode;
import org.dromara.llm.domain.vo.SysMessageVo;
import org.dromara.llm.domain.vo.SysTemplateVo;
import org.dromara.llm.llm.ChatService;
import org.dromara.llm.service.ISysTemplateService;
import org.dromara.llm.service.IAiChatService;
import org.dromara.llm.stt.factory.SttServiceFactory;
import org.dromara.llm.tts.factory.TtsServiceFactory;
import org.dromara.llm.util.AudioVoiceDetection;
import org.dromara.llm.vad.impl.VadService;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.util.concurrent.CompletableFuture;

@RequiredArgsConstructor
@Slf4j
@Service
public class AIChatServiceImpl implements IAiChatService {
    private final ChatService aiService;
    private final ISysTemplateService sysTemplateService;
    private final SttServiceFactory sttServiceFactory;
    private final TtsServiceFactory ttsServiceFactory;
    private final VadService vadService;

    /**
     * 通用聊天，不需要模板，不保存聊天记录
     *
     * @param message 消息内容
     */
    @Override
    public String chat(String message) {
        return aiService.chatText(message);
    }

    /**
     * 通用聊天(流式响应)，不需要模板，不保存聊天记录
     *
     * @param message 消息内容
     */
    @Override
    public Flux<ChatResponse> chatStream(String message) {
        return aiService.chatStream(message);
    }

    /**
     * 依据提示词模板聊天
     * 不保存聊天记录
     *
     * @param message    消息内容
     * @param templateId 模板ID
     */
    @Override
    public String chatByTemplate(String message, Long templateId) {
        SysTemplateVo sysTemplateVo = sysTemplateService.queryById(templateId);
        return aiService.chatBySystemTemplate(message, sysTemplateVo);
    }

    /**
     * 依据提示词模板聊天
     *
     * @param message    消息内容
     * @param deviceId   设备ID，用于保存聊天记录，区分是哪个设备
     * @param templateId 模板ID
     */
    @Override
    public String chatByTemplate(String message, Long templateId, String deviceId) {
        SysTemplateVo sysTemplateVo = sysTemplateService.queryById(templateId);
        String response = aiService.chatBySystemTemplate(message, sysTemplateVo);
        //保存聊天记录，线程异步执行，避免阻塞
        SysMessageVo messageVo = builderMessage(message, deviceId, null);
        CompletableFuture.runAsync(() -> saveSysMessageVo(messageVo, response, sysTemplateVo.getTtsFlag()));
        return response;
    }

    /**
     * 语音识别
     *
     * @param id
     * @param message 语音消息，以字节数组形式输入
     * @return 识别后的文本结果，如果无人声检测到则返回null
     */
    @Override
    public String audioRecognition(String id, byte[] message) {
        try {
            VadService.VadResult vadResult = vadService.detectVadFromPcm(id, message);
            if (vadResult.isSpeechActive()){
                // 如果检测到人声，则调用语音识别服务进行识别
                return sttServiceFactory.getDefaultSttService().recognition(message);
            }else {
                return null;
            }

//            // 读取PCM文件
//            int sampleRate = 16000; // 采样率 44.1kHz
//            int bitsPerSample = 16; // 16位深度
//            int channels = 1;       // 单声道
//            double threshold = 0.01; // 阈值，可根据实际环境调整
//            boolean hasVoice = new AudioVoiceDetection().hasVoice(message, sampleRate, bitsPerSample, channels, threshold);
//            log.info("检测结果：{}", hasVoice ? "有人说话" : "无人说话");
//            if (hasVoice) {
//                // 如果检测到人声，则调用语音识别服务进行识别
//                return sttServiceFactory.getDefaultSttService().recognition(message);
//            } else {
//                // 如果未检测到人声，则返回null
//                return null;
//            }
        } catch (Exception e) {
            // 记录人声检测异常
            log.info("人声检测异常{}", e.getMessage());
            return null;
        }
    }


    /**
     * 音频处理AI回复
     * <p>
     * 本方法主要用于处理音频信息，通过AI进行回复
     * 它首先从系统模板中获取特定的模板信息，然后根据该模板和输入的消息生成回复
     *
     * @param message 接收到的消息，将被用于生成AI回复
     * @return AI根据系统模板生成的回复消息
     */
    @Override
    public String audioProcessing(String message) {
        // 从系统模板服务中查询特定模板，这里硬编码了模板ID为4
        SysTemplateVo sysTemplateVo = sysTemplateService.queryById(4L);
        // 使用查找到的模板和输入消息，通过AI服务生成回复消息
        return aiService.chatBySystemTemplate(message, sysTemplateVo);
    }


    /**
     * 生成音频文件
     * <p>
     * 该方法用于将给定的文本消息转换为音频文件
     * 它利用文本转语音（TTS）服务来实现这个功能
     *
     * @param message 需要转换为音频的文本消息
     * @return 转换后的音频文件的路径或其他标识符
     * @throws Exception 如果在生成音频过程中发生错误，例如网络问题或TTS服务不可用
     */
    @Override
    public String generateAudio(String message) throws Exception {
        // 调用默认的TTS服务将文本转换为语音
        return ttsServiceFactory.getDefaultTtsService().textToSpeech(message);
    }


    /**
     * 保存消息到数据库
     * <p>
     * 此方法根据发送者类型（用户或助手）处理消息保存逻辑用户发送的消息不会立即进行语音转换，
     * 而是直接保存；助手回复的消息则直接使用文本转语音（TTS）生成的音频路径
     *
     * @param message   消息内容
     * @param deviceId  设备ID，用于标识消息来源
     * @param sender    消息发送者类型，可以是用户（USER）或助手（ASSISTANT）
     * @param audioPath 音频文件路径，仅当发送者为助手时使用
     */
    public void saveMessage(String message, String deviceId, LlmSender sender, String audioPath) {
        if (sender.equals(LlmSender.USER)) {
            // 保存聊天记录，线程异步执行，避免阻塞,用户发送的消息，不带语音转换
            SysMessageVo vo = builderMessage(message, null, LlmSender.USER);
            CompletableFuture.runAsync(() -> aiService.addMessage(vo, TtsFlagCode.F));
        } else {
            // 保存聊天记录，线程异步执行，避免阻塞，模型返回的消息，不带语音转换，直接使用TTS的语音路径
            SysMessageVo assistantVo = builderMessage(message, null, LlmSender.ASSISTANT);
            assistantVo.setAudioPath(audioPath);
            CompletableFuture.runAsync(() -> aiService.addMessage(assistantVo, TtsFlagCode.F));
        }
    }


    /**
     * 构建消息对象
     *
     * @param message  消息内容
     * @param deviceId 设备ID，用于保存聊天记录，区分是哪个设备
     * @return SysMessageVo
     */
    private SysMessageVo builderMessage(String message, String deviceId, LlmSender sender) {
        // 生成一个随机的SessionId，用于标识一个聊天对话（包含返回）
        String SessionId = UUID.randomUUID().toString();
        SysMessageVo messageVo = new SysMessageVo();
        messageVo.setSessionId(SessionId);
        messageVo.setMessageType("NORMAL");
        messageVo.setMessage(message);
        if (sender != null) {
            messageVo.setSender(sender.getCode());
        }
        messageVo.setRoleId(0L); // TODO 获取角色ID，后续依据业务修改
        messageVo.setDeviceId(deviceId);
        return messageVo;
    }

    /**
     * 通用翻译
     *
     * @param message    需要翻译的内容
     * @param sourceLang 源语言
     * @param targetLang 目标语言
     */
    @Override
    public String translate(String message, String sourceLang, String targetLang, String deviceId) {
        String prompt;
        if (targetLang == null || targetLang.isEmpty()) {
            log.info("Target language is null");
            return null;
        }

        if (targetLang.equals("chinese")) {
            targetLang = "中文";
        }

        if (sourceLang != null && sourceLang.equals("chinese")) {
            sourceLang = "中文";
        }

        // 构建提示信息，用于指定翻译任务
        if (sourceLang == null || sourceLang.isEmpty()) {
            prompt = String.format(
                "请将下列词语翻译成 %s, 不需要翻译说明: \"%s\"",
                targetLang, message
            );
        } else {
            prompt = String.format(
                "请将下列词语从 %s 翻译成 %s, 不需要翻译说明: \"%s\"",
                sourceLang, targetLang, message
            );
        }
        String response = aiService.chatText(prompt);
        // 保存翻译结果，带语音转换 ttsFlag = 1
        // 异步保存消息，不会阻塞当前线程。
        SysMessageVo messageVo = builderMessage(message, deviceId, null);
        CompletableFuture.runAsync(() -> saveSysMessageVo(messageVo, response, TtsFlagCode.F.getCode()));
        return response;
    }

    /**
     * 保存聊天内容
     *
     * @param messageVo 消息内容
     * @param response  大模型反馈结果
     * @param ttsFlag   语音转换标志
     */
    protected void saveSysMessageVo(SysMessageVo messageVo, String response, String ttsFlag) {
        int re = aiService.saveSentence(messageVo, response, ttsFlag);
        if (re > 0) {
            log.info("保存聊天记录成功:{}", re);
        } else {
            log.error("保存聊天记录失败:{}", re);
        }
    }


}
